<html>
<head><title>Calculator</title></head>
<body>
<h1>Calculator</h1>
<p>By James Hung (ID: 203762477)</p>
<p><b>Please enter an expression, and click Calculate.</b></p>
<form method="GET">
<input type="text" name="expression">
<input type="submit" value="Calculate">
</form>
<?php
	function print_result($message)
	{
		echo "<h2>Result</h2>" . $message;
	}

	function print_eval_error()
	{
		print_result("Error evaluating expression.");
	}

	/*
	 * To catch divison by zero and any unforseen evaluation errors.
	 */

	function myErrorHandler($errno, $errstr, $errfile, $errline)
	{
		print_eval_error();
		exit();
	}

	/*
	 * Retrieve expression input from user.
	 */

	$expression = $_GET["expression"];
	if (strlen($expression) <= 0)
		return;

	/*
	 * Validate expression based on some basic definitions and conventions of individual components of an expression.
	 */

	// Zero or more whitespace are allowed around each component.
	$whitespace = "\s*";

	// A number has (in the following order): zero or one negative sign, one or more digits, zero or one decimal, zero or more digits.
	$number     = $whitespace . "\-?\d+\.?\d*" . $whitespace;

	// An operator is exactly one of + - * /
	$operator   = $whitespace . "[+\-\*\/]" . $whitespace;

	// An operation is an operator followed by a number (right operand). We can be sure that will already be a left operand because the expression must start with a number.
	$operation  = $operator . $number;

	// An expression:
	// - Must start with a number.
	// - The number is followed by zero or more operations.
	if (!preg_match("#^" . $number . "(" . $operation . ")*" . "$#", $expression)) {
		print_result("Syntax error.");
		return;
	}

	/*
	 * Ensure each + - * / is surrounded by whitespace. That way, something like 1--2 or 1 --2 would become 1 - - 2, which would be interpreted by eval as 1 - (-2) as expected.
	 */

	$new_expression = "";

	for ($i = 0; $i < strlen($expression); $i++) {
		switch ($expression[$i]) {
			case '+':
			case '-':
			case '*':
			case '/':
				$new_expression .= ' ' . $expression[$i] . ' ';
				break;
			default:
				$new_expression .= $expression[$i];
				break;
		}
	}

	/*
	 * Do the actual calculations and output the result.
	 */

	set_error_handler("myErrorHandler");
	$ret = eval("\$answer = $new_expression; return TRUE;");
	restore_error_handler();

	if ($ret == FALSE) {
		print_eval_error();
		return;
	}

	print_result($expression . " = " . $answer);
?>
</body>
</html>
